perm filename MAINPR.SAI[PNT,HE]16 blob sn#466131 filedate 1979-08-13 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00019 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	initial declarations and global variables
C00005 00003	! facilities:   error messages,syntax explanations,error,abort1
C00011 00004	! parsing procedures
C00012 00005	! recover,frcver
C00015 00006	! Readcode,helprequest
C00021 00007	! symbol table: check,checktot,ensym,delsym,newsym,oldsym
C00028 00008	! symbol table: mk_pr, mk_rec, mk_sym, symtree routines
C00036 00009	! symbol table: gtframe,checkoff,arrydim
C00039 00010	! symbol table: nwr,dcdsym,unlink,linkfr,nwarec
C00044 00011	! symbol table: control,insertion
C00050 00012	! symbol table: killtree,killvar,reset
C00053 00013	! assignment instruction
C00055 00014	! tree operations:   afx_node,ufx_node,copycode,copy,copy_tree
C00061 00015	! arm interactions:  read_pos,readarm,frasg,arm_check
C00064 00016	! arm interactions:  fconstructproc
C00068 00017	! system facilities: editcode,renmcode,bailcode,qbailcode
C00075 00018	! parse procedures: other
C00078 00019	! main program
C00086 ENDMK
C⊗;
comment initial declarations and global variables;

DEFINE $MAINPR=TRUE ;
DEFINE #NOFUNCT=TRUE; COMMENT ELIMINATE FUNCTIONS IN THIS VERSION;
REQUIRE 300 STRING_PDL;REQUIRE 1000 SYSTEM_PDL;
REQUIRE 10000 STRING_SPACE;
REQUIRE "HEADER.SAI" SOURCE_FILE;

IFC #DEBUG THENC
	REQUIRE "PRINTX.HDR[AL,HE]" SOURCE_FILE;
	!	FOR PRINTING OUT RECORDS ;
	! BAIL BUG REQUIRES FOLLOWING DUMMY PROCEDURE;
	PROCEDURE BAIL_ANAMOLY;
	BEGIN PRINTX(3); RECPRN(F_WRLD);TBLKSUPPRESS(NULL);SETRPM(0,0); END;
ENDC

LABEL MAINL;			! used by abort procedures to go to the top level;


PRESET_WITH NULL,"SCALAR","VECTOR","ROT","TRANS","FRAME","MACRO","FUNCTION";
INTERNAL STRING ARRAY $DTYPE[0:7];


! facilities:   error messages,syntax explanations,error,abort1;

! INTERNAL INTEGER $HELP;					! used by error;

	! error messages for syntactic errors;

PRESET_WITH
	"--→ ; ",
	"--→ , ",
	"--→ . ",
	"--→ [ ",
	"--→ ] ",
	"--→ ( ",
	"--→ ) ",
	"--→ + ",
	"--→ * ",
	"--→ ALONG ",
	"--→ BY ",
	"--→ INTO ",
	"--→ REL ",
	"--→ ROT ",
	"--→ TO ",
	"--→ TRANS ",
	"--→ WRT ",
	"--→ XHAT or YHAT or ZHAT ",
	"--→ YARM or BARM ",
	"--→ YHAND or BHAND ",
	"--→ INPUT after ↑, ↓, ∨, ∧, <, >",
 	"--→ identifier ",
	"--→ number ",
	"--→ file name ",
        "--→ arithmetic operator ",
	"required ←--",
	"--→ error in explicit ",
	"vector ←--",
	"rotation ←--",
	"frame ←--",
	"--→ affix_type is wrong ←--",
	"--→ wrong identifier or wrong number ←--",
	"--→ unrecognized instruction ←--",
	"| ",
	"VECTOR required after DISTANCE",
	"--→ undeclared identifier ";
INTERNAL STRING ARRAY $SYNMSG[0:35];

	! error messages used for semantic errors;
	! the first messages cannot be moved in another position because they 
	  are referred to using the type of the variables(#SC,#VT,#RT,@TR,@FR);

PRESET_WITH
	" scalar not existent ",		
        " vector not existent ",	
	" rotation not existent ",
	" trans not existent ",
        " frame not existent ",	
	" is not scalar nor vector nor rotation ",
	" object not existent ",		
	" out of symbol table, delete some variables and try again",
	" cannot be moved ",
	" already defined symbol ",
	" dismatching of types ",
	" affixed frame ",
	" reading on arm required ",
	" instruction not executed",
	" is a POINTY defined variable or constant and cannot be changed";
INTERNAL STRING ARRAY $SEMSG[0:14];




INTERNAL simple procedure esc_I;
	$esc_I←true;


	! called after syntax error. If required gives explanation of the error;

INTERNAL PROCEDURE ERROR(STRING ERR1,ERR2(NULL));
	BEGIN
	STRING ANSWER;
	INTEGER I,J;
	I ← LENGTH($CLINR);
	J ← LENGTH($CLNE);
	PRINT($CLNE[1 TO J-I]&LF&$CLINR,CRLF);
	PRINT (ERR1,ERR2,CRLF);
ifc false thenc to temporarily destroy
	PRINT("    ",TOKEN,"     ",$CLINR,IFC #HELP THENC "(? for more explanation)"
			ELSEC CRLF ENDC);
	IFC #HELP THENC 
		ANSWER←INCHRW;IF ANSWER=CR THEN INCHRW;
		OUTSTR(CRLF);
		IF ANSWER="?" THEN HLPMSG($HELP);	! if required gives explanations;
	ENDC
endc	IFC #DISPL THENC
		IF DEVICE≠DSK_X THEN $ALLOW←0;	! while reading display is not updated;
	ENDC
	ESC_P;
	LODED($CLNE&CR);		! so it is possible to correct the command;
	$CLINR←NULL; STOKEN←FALSE;
	GO TO MAINL;			! goes to the main loop;
	END;


	! called after unrecoverable semantic error;

INTERNAL PROCEDURE ABORT1(STRING NAME,ERROR(NULL));
	BEGIN
	PRINT (NAME,ERROR,CRLF);
	IFC #DISPL THENC
		IF DEVICE≠DSK_X THEN $ALLOW←0;	! while reading display is not updated;
	ENDC
! ***	PRINT("* ");ESC_P;
	LODED($CLNE&CR);		! so it is possible to correct the command;
	$CLINR←NULL; STOKEN←FALSE;
	GO TO MAINL;			! goes to the main loop;
	END;

INTERNAL PROCEDURE CHKESC_I;
	IF $ESC_I THEN
		BEGIN
		MTYDEVSTACK;
		PRINT("
<ESCAPE> I termination
");
		$ESC_I←FALSE;	ENABLE(15);	! reset it again;
		$ELFABORTED←TRUE;
		GOTO MAINL;
		END;
! parsing procedures;


	! saves important parts of last instruction, for default instructions.
	  Oldobj is used to pass to gettoken the value corresponding to the ⊗;

INTERNAL PROCEDURE OLDSAV(STRING CMD,OBJ);
	BEGIN
	OLDCMD←CMD;
	OLDOBJ←OBJ;
	END;
! recover,frcver;

	! called when an indefined variable is used. Tries to recover, asking
	  the correct name of the variable, and returns it.
	  (null string or <control-C> to return to the main loop);


STRING PROCEDURE RECOVER(STRING SYMB);
BEGIN "R"
STRING ANSWER;LABEL CC;
	! you can change the identifier symb;
CC:
LODED(SYMB&CR);
ANSWER←INCHWL;				! reads new identifier;
IFC #OUTPT THENC
	IF $OUT THEN CPRINT($TTYCH,ANSWER,CRLF);
ENDC
SYMB←SCAN(ANSWER,$ERRTAB,$BRCHR);	! eliminates blanks and checks break;
IF $BRCHR≠0 AND $BRCHR≠'40
   THEN BEGIN
	PRINT("break character found. Try again ");
        GO TO CC;			! so... you can try again;
    	END
   ELSE IF SYMB THEN RETURN(SYMB);	! a "good" symbol is returned;
	! you want to delete the instruction being interpreted;
CLRBUF;
	IFC #DISPL THENC
		IF DEVICE≠DSK_X THEN $ALLOW←0;	! while reading display is not updated;
	ENDC
PRINT($SEMSG[13],CRLF,"* ");
ESC_P;
GO TO MAINL;				! goes to the main loop;
END "R";


IFC #OUTPT THENC

	! allows recovering if a file not available has been required
	  (null string or <control-C> to return to the main loop);

INTERNAL STRING PROCEDURE FRCVER(STRING FILE);
	BEGIN "F"
	LODED(FILE&CR); 
	ASKUSER;
	IFC #OUTPT THENC
		IF $OUT THEN CPRINT($TTYCH,$CLINR,CRLF);
	ENDC
	IF $CLINR
	   THEN RETURN(NAMEFILE)                
	   ELSE BEGIN
		CLRBUF;
		IFC #DISPL THENC
 			IF DEVICE≠DSK_X THEN $ALLOW←0;	! while reading display is not updated;
		ENDC
		PRINT($SEMSG[13],CRLF,"* ");
		ESC_P;
		GO TO MAINL;			! goes to the main loop;
		END;
	END "F";
ENDC						  
! Readcode,helprequest;

IFC #OUTPT THENC

	! these procedures used to read from a file are here and not in 
	  the input/output module becuase the READEXEC procedure calls
	   the PARSE procedure  for each instruction;

	! the above comment is no longer true, since READEXEC no longer
	  exists.  However, they should be shifted to the input/output module
	  when some rational means to keep track of I/0 is settled upon.
	  I think what is wanted is a file record that it used to keep
	  all the information related to each file ;

INTERNAL PROCEDURE READCODE(STRING FID; BOOLEAN ECHO(FALSE));
	BEGIN
	PUSHDEVSTACK;
	OPEN($INPCH←GETCHAN,"DSK",0,3,0,1000,$BRCHR,$EOF);
	LOOKUP($INPCH,FID,$EOF);
	WHILE $EOF
	     DO	BEGIN
		PRINT("enter failed");
		FID←FRCVER(FID);
		LOOKUP($INPCH,FID,$EOF);
		END;
	IFC #DISPL THENC $ALLOW←$ALLOW+1; IF ECHO THEN DPYFREE; $SCLST←NULL; ! to force update; ENDC
	DEVICE←DSK_X;

	NEWFILE←TRUE; FILEPRINT←ECHO;
 	END;

CLEANUP FCLOSE;

ELSEC
INTERNAL PROCEDURE UPDATE;;
ENDC

	! called after reading ?. Gives some information, erasing the display;

IFC #HELP THENC 
	INTERNAL PROCEDURE HELPREQUEST;
	BEGIN "H"
	IFC #DISPL THENC DPYFREE;ENDC
		! reads the comand after ?, if there is;
!	$TAIL←SCAN($LINE,$SCNTAB,$BRCHR);
!	HLPDO($TAIL);					! in HELP.SAI[1,MLG];
	hlpmsg($help);
	ASKUSER;
	HLPDO($clinr);
	$clinr←$clne←null;
	IFC #DISPL THENC UPDATE;ENDC
	END "H";
ELSEC
INTERNAL PROCEDURE HELPREQUEST;;
ENDC

! symbol table: check,checktot,ensym,delsym,newsym,oldsym;
	! checks if symbol symb, of type nm, is in symbol table in the class nm,
	  and return its pointer;

INTERNAL RPTR(SYMBOL) PROCEDURE CHECK(STRING SYMB;INTEGER NM);
	BEGIN
	RPTR(SYMBOL) TEMP;INTEGER IND,I;
	IND←$ENTRY[NM];		! address of last record of type nm filled;
	FOR I← 1 STEP 1 UNTIL IND DO
	    IF (TEMP←$YMTAB[NM,I])≠NULL_RECORD AND EQU(SYMBOL:PNAME[TEMP],SYMB) 
		       THEN RETURN(TEMP);
	RETURN(NULL_RECORD);			! symbol not found;
	END;

 	! checks if symbol symb is in symbol table, determines its class and
	  return its pointer;

INTERNAL RPTR(SYMBOL) PROCEDURE CHECKTOT(STRING SYMB);
	BEGIN
	INTEGER K;RPTR(SYMBOL)TEMP;
	FOR K←#MIN STEP 1 UNTIL #MAX DO
	    IF (TEMP←CHECK(SYMB,K))≠NULL_RECORD 
	       THEN RETURN(TEMP);
	RETURN(NULL_RECORD);			! symbol not found;
	END;


	! enters the symbol symb and the pointer to its node in symbol table,
	  in the class nm. The record of the class SCALAR,VECTOR,ROT,TRANS or
	  FRAME has to be constructed before calling ENSYM;


INTEGER PROCEDURE NEW_OFFSET(INTEGER NM);
	BEGIN
	INTEGER I;
	IF NM≠#MC THEN
	IF OFFSET[CUR_OFFSET,NM]=OFFSET[MAX_OFFSET,NM] THEN ERROR("NO MORE SPACE FOR NEW SYMBOLS IN 11");
	IF #SC≤NM≤#VT OR #MC≤NM≤#PR
		THEN OFFSET[CUR_OFFSET,NM]←OFFSET[CUR_OFFSET,NM]+1
		ELSE FOR I← 3 STEP 1 UNTIL 5 DO OFFSET[CUR_OFFSET,I]←OFFSET[CUR_OFFSET,I]+1;
	RETURN(OFFSET[CUR_OFFSET,NM]);
	END;

INTERNAL RPTR(SYMBOL) PROCEDURE ENSYM(STRING SYMB;INTEGER NM;RANY VAL;
	RPTR(SYMBOL)OLDREC(NULL_RECORD); INTEGER ACCESS(#SIMPLE));
	BEGIN
	RPTR (SYMBOL) TEMP;INTEGER IND;
	IF $ENTRY[NM]≥#LTYPE 
	   THEN ABORT1($SEMSG[7]);	! out of symbol table;
	IF OLDREC THEN TEMP←OLDREC ELSE	TEMP←NEW_RECORD(SYMBOL);
	$YMTAB[NM,$ENTRY[NM]←$ENTRY[NM]+1]←TEMP; ! pointer to the new record in $YMTAB;
!		SYMBOL:VALID[TEMP]←TRUE;
	SYMBOL:TYPE[TEMP]←NM;
	SYMBOL:PNAME[TEMP]←SYMB;	! pname of symbol;
	SYMBOL:OBJECT[TEMP]←VAL;	! pointer to the record previously created;
	IF ACCESS=#SIMPLE AND #SC≤NM≤#FR THEN
		BEGIN  SYMBOL:INDEX[TEMP]←NEW_OFFSET(NM);
		       SYMBOL:OFFSET[TEMP]←ARROFF[NM];
		END
	ELSE IF NM=#MC THEN SYMBOL:INDEX[TEMP]←NEW_OFFSET(NM);
	RETURN(TEMP);
	END;


INTERNAL PROCEDURE ENSYM$(RPTR(SYMBOL)SYM; INTEGER NM(0));
	BEGIN
	INTEGER IND;
	IF NM=0 THEN NM←SYMBOL:TYPE[SYM]
		ELSE SYMBOL:TYPE[SYM]←NM;
	IF $ENTRY[NM]≥#LTYPE 
	   THEN ABORT1($SEMSG[7]);	! out of symbol table;
	$YMTAB[NM,$ENTRY[NM]←$ENTRY[NM]+1]←SYM;		! pointer to the new record in $YMTAB;
	IF SYMBOL:ACCESS[SYM]=#SIMPLE AND #SC≤NM≤#FR THEN
		BEGIN  SYMBOL:INDEX[SYM]←NEW_OFFSET(NM);
		       SYMBOL:OFFSET[SYM]←ARROFF[NM];
		END
	ELSE IF NM=#MC THEN SYMBOL:INDEX[SYM]←NEW_OFFSET(NM);
	END;

	! returns a new symbol, if symb is present in $YMTAB. Id used before 
	  inserting a new symbol in $YMTAB to be sure that a symbol with the 
	  name has not been defined before. This procedure allows recovering;

STRING PROCEDURE NEWSYM(STRING SYMB);
	BEGIN
	RPTR(SYMBOL)TEMP;
	! if there is a symbol with the same pname allows recovering;
	WHILE (TEMP←CHECKTOT(SYMB))≠NULL_RECORD 
	     DO BEGIN
	        PRINT(SYMB,$SEMSG[9]); 
		SYMB←RECOVER(SYMB);
		END;
	RETURN(SYMB);
	END;

	! checks if symb is present in $YMTAB and returns its pointer and its
	  type (using the reference variable obtype), otherwise allows recovering.
	  Is used when the symbol required has to be present in $YMTAB (ex. 
	  in EDIT or RENAME instruction);

INTERNAL RPTR(SYMBOL) PROCEDURE OLDSYM(REFERENCE STRING SYMB;REFERENCE INTEGER OBTYPE);
	BEGIN
	RPTR(SYMBOL)EL;
	EL←CHECKTOT(SYMB);
	! if symbol is not in $YMTAB, recovering is allowed;
	WHILE (EL←CHECKTOT(SYMB))=NULL_RECORD
	     DO BEGIN
		PRINT ($SEMSG[6]);
		SYMB←RECOVER(SYMB);
		END;
	OBTYPE←SYMBOL:TYPE[EL];
	RETURN(EL);
	END;


PROCEDURE DELSYM(RPTR(SYMBOL)EL);
	BEGIN
	INTEGER ADDRFN,I;
	INTEGER OBTYPE; OBTYPE←SYMBOL:TYPE[EL];
	ADDRFN← $ENTRY[OBTYPE];	! final addr. in $YMTAB for class;
	FOR I←1 STEP 1 UNTIL ADDRFN DO
	IF $YMTAB[OBTYPE,I]=EL 
	   THEN BEGIN
	 	$YMTAB[OBTYPE,I]←$YMTAB[OBTYPE,ADDRFN];
		$ENTRY[OBTYPE]←ADDRFN-1;	! move last element into hole;
!		SYMBOL:VALID[EL]←FALSE;
		DONE;
		END;
	END;

! symbol table: mk_pr, mk_rec, mk_sym, symtree routines;

	! produces a symbol record with certain fields filled in ;
INTERNAL RPTR(SYMBOL)PROCEDURE MK_SYM(STRING PNAME; INTEGER TYPE;
		RANY PTR(NULL_RECORD); INTEGER ACCESS(#SIMPLE));
	BEGIN
	RPTR(SYMBOL)SYM;
	SYM←NEW_RECORD(SYMBOL);
	SYMBOL:PNAME[SYM]←PNAME;
	SYMBOL:TYPE[SYM]←TYPE;
	SYMBOL:OBJECT[SYM]←PTR;
	SYMBOL:ACCESS[SYM]←ACCESS;
	RETURN(SYM);
	END;

INTERNAL RPTR(PROC)PROCEDURE MK_PR(INTEGER ARGS; STRING ARRAY ARGNAME;
			INTEGER ARRAY ARGTYPE,ARGACCS,ARGDIM);
IF ARGS=0 THEN RETURN(NEW_RECORD(PROC)) ELSE
	BEGIN
	RPTR(PROC)E;
	STRING ARRAY S[1:ARGS];
	INTEGER ARRAY T,C,D[1:ARGS];
	ARRTRAN(S,ARGNAME);
	ARRTRAN(T,ARGTYPE);
	ARRTRAN(C,ARGACCS);
	ARRTRAN(D,ARGDIM);
	E←NEW_RECORD(PROC);
	PROC:NARGS[E]←ARGS;
	MEMORY[LOCATION(PROC:ARGNAME[E])]↔MEMORY[LOCATION(S)];
	MEMORY[LOCATION(PROC:ARGDIM[E])]↔MEMORY[LOCATION(D)];
	MEMORY[LOCATION(PROC:ARGACCS[E])]↔MEMORY[LOCATION(C)];
	MEMORY[LOCATION(PROC:ARGTYPE[E])]↔MEMORY[LOCATION(T)];
	RETURN(E);
	END;
IFC NOT #NOFUNCT THENC
INTERNAL RPTR(FUNCTION) PROCEDURE MK_FN(INTEGER ARGS);
	BEGIN
	RPTR(SCALAR,VECTOR,ROT,TRANS,FRAME,FUNCTION) ARRAY P[0:ARGS];
	STRING ARRAY S[0:ARGS]; 	INTEGER ARRAY I[0:ARGS];
	RPTR(FUNCTION)F;		F←NEW_RECORD(FUNCTION);
	FUNCTION:NARGS[F]←ARGS;
		MEMORY[LOCATION(FUNCTION:ARGNAME[F])]←MEMORY[LOCATION(S)];
		MEMORY[LOCATION(FUNCTION:PTR[F])]←MEMORY[LOCATION(P)];
		MEMORY[LOCATION(FUNCTION:ARGTYPE[F])]←MEMORY[LOCATION(I)];
		MEMORY[LOCATION(I)]←
		MEMORY[LOCATION(P)]←MEMORY[LOCATION(S)]←0;
	RETURN(F);
	END;
ENDC
INTERNAL RPTR(SCALAR,VECTOR,ROT,TRANS,FRAME) PROCEDURE MK_REC(INTEGER TYPE);
	BEGIN
	RANY TEMP;
	REAL ARRAY XF[1:6];
	CASE TYPE OF 
	begin "case"
	[#SC] TEMP←NEW_RECORD(SCALAR);
	[#VT] TEMP←NEW_RECORD(VECTOR);
	[#RT] BEGIN
		TEMP←NEW_RECORD(ROT);
		MEMORY[LOCATION(ROT:XF[TEMP])]←MEMORY[LOCATION(XF)];
		END;
	[#TR] BEGIN
		TEMP←NEW_RECORD(TRANS);
		MEMORY[LOCATION(TRANS:XF[TEMP])]←MEMORY[LOCATION(XF)];
		END;
	[#FR] BEGIN
		TEMP←NEW_RECORD(FRAME);
		MEMORY[LOCATION(FRAME:XF[TEMP])]←MEMORY[LOCATION(XF)];
! insert here the affixment to the world;
		FRAME:HOWLINKED[TEMP]←#INDLK;		! independently;
		END;
!	[#MC]	TEMP←NEW_RECORD(MACRO);
	[#FN]	TEMP←NEW_RECORD(PROC);
	ELSE	ERROR("PARSER ERROR, NO SUCH RECORD CLASS IN MK_REC")
	end "case";
	MEMORY[LOCATION(XF)]←0;
	RETURN(TEMP);
	END;

RPTR(SYMTREE)PROCEDURE MK_SYMTREE(RPTR(SYMBOL)S);
	BEGIN
	RPTR(SYMTREE)E;
	SYMTREE:SYM[E←NEW_RECORD(SYMTREE)]←S;
	RETURN(E);
	END;

RECURSIVE PROCEDURE INSRTTREE(RPTR(SYMBOL)S; RPTR(SYMTREE)STREE);
	BEGIN
	RPTR(SYMTREE)SS;
	CASE COMPEQU(SYMBOL:PNAME[S],SYMBOL:PNAME[SYMTREE:SYM[STREE]])+1 OF
		BEGIN
		[-1+1]	IF (SS←SYMTREE:LLINK[STREE])=NULL_RECORD
				THEN SYMTREE:LLINK[STREE]←MK_SYMTREE(S)
				ELSE INSRTTREE(S,SS);
		[0+1]	ERROR("ugh trying to insert element ");
		[1+1]	IF (SS←SYMTREE:RLINK[STREE])=NULL_RECORD
				THEN SYMTREE:RLINK[STREE]←MK_SYMTREE(S)
				ELSE INSRTTREE(S,SS)
		END;
	END;

INTERNAL PROCEDURE INSERTSYMTREE(RPTR(SYMBOL)S;RPTR(BLOCKREC)STREE);
	BEGIN
	IF BLOCKREC:TREE[STREE]=NULL_RECORD
	  THEN BLOCKREC:TREE[STREE]←MK_SYMTREE(S)
	  ELSE INSRTTREE(S,BLOCKREC:TREE[STREE]);
	BLOCKREC:#ARGS[STREE]←BLOCKREC:#ARGS[STREE]+1;
	END;

INTERNAL RPTR(BLOCKREC)PROCEDURE BLOCKIFY(INTEGER NARGS; RPTR(SYMBOL)ARRAY SYMARR;
		RPTR(BLOCKREC)BLOCK(NULL_RECORD));
	BEGIN INTEGER I;
	RPTR(BLOCKREC)BLOCKPTR;
	IF BLOCK THEN BLOCKPTR←BLOCK ELSE BLOCKPTR←NEW_RECORD(BLOCKREC);
	FOR I←1 STEP 1 UNTIL NARGS DO
		INSERTSYMTREE(SYMARR[I],BLOCKPTR);
	RETURN(BLOCKPTR);
	END;

RPTR(SYMBOL)RECURSIVE PROCEDURE SEARCHSYMTREE(STRING S; RPTR(SYMTREE)STREE);
    IF STREE=NULL_RECORD
	THEN RETURN(NULL_RECORD)
	ELSE CASE COMPEQU(S,SYMBOL:PNAME[SYMTREE:SYM[STREE]]) +1 OF
		BEGIN
		[-1+1]	RETURN(SEARCHSYMTREE(S,SYMTREE:LLINK[STREE]));
		[0+1]	RETURN(SYMTREE:SYM[STREE]);
		[1+1]	RETURN(SEARCHSYMTREE(S,SYMTREE:RLINK[STREE]))
		END;

INTERNAL RPTR(SYMBOL)PROCEDURE SEARCHBLOCK(STRING S; RPTR(BLOCKREC)R);
	RETURN(SEARCHSYMTREE(S,BLOCKREC:TREE[R]));

RPTR(SYMBOL)RECURSIVE PROCEDURE SEARCHSYMTREEOFF(INTEGER I; RPTR(SYMTREE)STREE);
	IF STREE=NULL_RECORD
	    THEN RETURN(NULL_RECORD)
	    ELSE IF I=SYMBOL:OFFSET[SYMTREE:SYM[STREE]]
		THEN RETURN(SYMTREE:SYM[STREE])
		ELSE BEGIN
			RPTR(SYMBOL)S;
			IF S←SEARCHSYMTREEOFF(I,SYMTREE:LLINK[STREE])
			    THEN RETURN(S)
			    ELSE RETURN(SEARCHSYMTREEOFF(I,SYMTREE:RLINK[STREE]))
		     END;

RPTR(SYMBOL)PROCEDURE SEARCHBLOCKOFF(INTEGER I; RPTR(BLOCKREC)R);
	IF R THEN RETURN(SEARCHSYMTREEOFF(I,BLOCKREC:TREE[R]))
		ELSE RETURN(NULL_RECORD);
! symbol table: gtframe,checkoff,arrydim;

INTERNAL RPTR(FRAME) PROCEDURE GTFRAME(INTEGER LEVOFF,#DIM; INTEGER ARRAY DIM;
				RPTR(SYMBOL)S);
	IF LEVOFF=ARROFF[#FR] THEN
		BEGIN
		RPTR(SYMBOL)TEMP;
		INTEGER I;
		FOR I←1 STEP 1 UNTIL $ENTRY[#FR] DO
			IF DIM[1]=SYMBOL:INDEX[TEMP←$YMTAB[#FR,I]] THEN
				RETURN(SYMBOL:OBJECT[TEMP]);
		RETURN(NULL_RECORD);
		END
	ELSE BEGIN "array or temporary"
		! not quite reight, this only assumes arrays;
		RPTR(ARRAYREC)ARR;
		INTEGER I,J;
		IF NOT S THEN ERROR("ERROR n GTFRAME: cant handle temporary variables yyet");
		ARR←SYMBOL:OBJECT[S];
		J←0;
		FOR I←1 STEP 1 UNTIL #DIM
			DO J←J+(DIM[I]-ARRAYREC:LB[ARR][I])*ARRAYREC:MUL[ARR][I];
		RETURN(SYMBOL:OBJECT[ARRAYREC:PTR[ARR][J+1]]);
	     END "array or temporary";

	! returns the symbol for given offset;
RPTR(SYMBOL) PROCEDURE CHECKOFF(INTEGER LEVOFF);
	BEGIN
	RPTR(SYMBOL) TEMP; INTEGER I,J;
	IF CURBLOCK AND TEMP←SEARCHBLOCKOFF(LEVOFF,CURBLOCK) THEN RETURN(TEMP);
	FOR I←#SC STEP 1 UNTIL #FR DO
		FOR J←1 STEP 1 UNTIL $ENTRY[I]
		DO IF (TEMP←$YMTAB[I,J]) AND SYMBOL:OFFSET[TEMP]=LEVOFF
			THEN RETURN(TEMP);
	RETURN(NULL_RECORD);
	END;

	! returns number of dimensions in symbol table for the leveloffset given;
INTERNAL INTEGER PROCEDURE ARRYDIM(INTEGER LEVOFF;REFERENCE RPTR(SYMBOL) SYM);
	BEGIN
	SYM←NULL_RECORD;
	IF LEVOFF=ARROFF[#SC] OR LEVOFF=ARROFF[#VT] OR LEVOFF=ARROFF[#RT]
		OR LEVOFF=ARROFF[#TR] OR LEVOFF=ARROFF[#FR]
		THEN RETURN(1)
		ELSE IF SYM←CHECKOFF(LEVOFF)
			THEN IF SYMBOL:ACCESS[SYM]=#SIMPLE THEN RETURN(0)
			ELSE RETURN(ARRAYREC:#DIM[SYMBOL:OBJECT[SYM]])
		ELSE RETURN(0);
	END;

! symbol table: nwr,dcdsym,unlink,linkfr,nwarec;

PROCEDURE UNLINK(RPTR(FRAME) N);
	BEGIN
	RPTR(FRAME) Y,E;
 	E←FRAME:EBRO[N];
 	IF (Y←FRAME:YBRO[N])≠NULL_RECORD 
	   THEN FRAME:EBRO[Y]←E
	   ELSE IF FRAME:DAD[N]≠NULL_RECORD THEN FRAME:SON[FRAME:DAD[N]]←E;
	IF E≠NULL_RECORD THEN FRAME:YBRO[E]←Y;
 	FRAME:EBRO[N]←NULL_RECORD;
 	FRAME:YBRO[N]←NULL_RECORD;
 	FRAME:DAD[N]←NULL_RECORD;
	END;

BOOLEAN PROCEDURE IS_ANCESTOR(RPTR(FRAME) N,D);
	BEGIN
	WHILE N≠NULL_RECORD DO
		IF N=D	THEN RETURN(TRUE) 
			ELSE N←FRAME:DAD[N];
	RETURN(FALSE);
	END;

	! sets #UP pointer structure in frame tree for N to be a child of D;

INTERNAL PROCEDURE LINKFR(RPTR(FRAME) N,D);	
	BEGIN
	IF NOT(D=F_WRLD AND FRAME:HOWLINKED[N]=#INDLK) 
	   THEN IF IS_ANCESTOR(D,N)
 		   THEN ABORT1(" backwards affixment to",frame:pname[D]);
        IF FRAME:DAD[N]≠NULL_RECORD THEN UNLINK(N);
 	IF (FRAME:EBRO[N]←FRAME:SON[D])≠NULL_RECORD THEN
 		FRAME:YBRO[FRAME:EBRO[N]]←N;
 	FRAME:YBRO[N]←NULL_RECORD;
 	FRAME:DAD[N]←D;
 	FRAME:SON[D]←N;
	END;


INTERNAL RPTR(TRANS) PROCEDURE ABSLOC(RPTR(FRAME) ND);
	BEGIN
	PRINT("DUMMY ABSLOC"); RETURN(NULL_RECORD);	END;

RPTR(SYMBOL)PROCEDURE NWR(STRING SYMB; INTEGER TYP);
	BEGIN
	RPTR(SCALAR,VECTOR,ROT,TRANS,FRAME)VAL; RPTR(SYMBOL)TEMP;
	SYMB←NEWSYM(SYMB);
	VAL←MK_REC(TYP);
	TEMP←ENSYM(SYMB,TYP,VAL);
	IF TYP=#FR THEN BEGIN FRAME:PNAME[VAL]←SYMB;
			IF TEMP≠ WORLD THEN LINKFR(VAL,F_WRLD);
			FRAME:PNAME[VAL]←SYMB;
			FRAME:HOWLINKED[VAL]←#INDLK;
			FRAME:SYM[VAL]←TEMP;
			END;
	$DISPLAYLIST[TYP]←NULL;
	RETURN(TEMP);
	END;

	! like nwr but does not insert into symbol table;
INTERNAL RPTR(SYMBOL)PROCEDURE NNWR(STRING SYMB; INTEGER TYP; INTEGER ACCESS(0));
	BEGIN
	RPTR(SCALAR,VECTOR,ROT,TRANS,FRAME)VAL; RPTR(SYMBOL)TEMP;
	TEMP←MK_SYM(SYMB,TYP,VAL←MK_REC(TYP),ACCESS);
	IF TYP=#FR THEN BEGIN
			IF TEMP≠ WORLD THEN LINKFR(VAL,F_WRLD);
			FRAME:PNAME[VAL]←SYMB;
			FRAME:HOWLINKED[VAL]←#INDLK;
			FRAME:SYM[VAL]←TEMP;
			END;
	RETURN(TEMP);
	END;


INTERNAL RPTR(SYMBOL)PROCEDURE NWAREC(RPTR(SYMBOL)TEMP;INTEGER #EL;
		INTEGER ARRAY LB,UB,MULT);
	BEGIN
	RPTR(ARRAYREC)VAL;
	INTEGER TYP,#DIM;
	VAL←SYMBOL:OBJECT[TEMP];
	TYP←SYMBOL:TYPE[TEMP];
	#DIM←ARRAYREC:#DIM[VAL];
		BEGIN
		INTEGER ARRAY ALB,AUB,MUL[1:5];
		INTEGER ARRAY I[1:5];
		INTEGER J,JJ;
		STRING S1,S2;
		RPTR(SYMBOL) ARRAY PTR[1:#EL];
		ARRBLT(ALB[1],LB[1],#DIM);
		ARRBLT(AUB[1],UB[1],#DIM);
		ARRBLT(MUL[1],MULT[1],#DIM);
		S1←SYMBOL:PNAME[TEMP]&"[";
		JJ←0;
		FOR I[1]←LB[1] STEP 1 UNTIL UB[1] DO
		FOR I[2]←LB[2] STEP 1 UNTIL UB[2] DO
		FOR I[3]←LB[3] STEP 1 UNTIL UB[3] DO
		FOR I[4]←LB[4] STEP 1 UNTIL UB[4] DO
		FOR I[5]←LB[5] STEP 1 UNTIL UB[5] DO
			BEGIN
			S2←S1&CVS(I[1]);
			FOR J←2 STEP 1 UNTIL #DIM DO
				S2←S2&","&CVS(I[J]);
			S2←S2&"]";
			PTR[JJ←JJ+1]←NNWR(S2,TYP,#ARRAY_ELEMENT);
			END;
		ARRAYREC:#EL[VAL]←#EL;
		MEMORY[LOCATION(ARRAYREC:PTR[VAL])]↔MEMORY[LOCATION(PTR)];
		MEMORY[LOCATION(ARRAYREC:LB[VAL])]↔MEMORY[LOCATION(ALB)];
		MEMORY[LOCATION(ARRAYREC:UB[VAL])]↔MEMORY[LOCATION(AUB)];
		MEMORY[LOCATION(ARRAYREC:MUL[VAL])]↔MEMORY[LOCATION(MUL)];
		END;
	RETURN(TEMP);
	END;
! symbol table: control,insertion;

RPTR(SYMBOL)PROCEDURE CNVRTR(RPTR(SYMBOL)EL;STRING SYMB);
	BEGIN
	RPTR(TRANS) TEMP;
	TEMP←SYMBOL:OBJECT[EL];
	DELSYM(EL);
	EL←NWR(SYMB,#FR);
	ARRTRAN(FRAME:XF[SYMBOL:OBJECT[EL]],TRANS:XF[TEMP]);
	$FRLST←$TRLST←NULL;
	RETURN(EL);
	END;

	! if the symbol symb is present in $YMTAB in the class OBTYPE returns
	  the pointer to it, otherwise allows recovering. The symbol is passed 
	  by reference so after recovering the new symbol is sent back;

RPTR(SYMBOL) PROCEDURE BELONGS2(REFERENCE STRING SYMB;INTEGER OBTYPE);
	BEGIN
	RPTR(SYMBOL) EL;
	EL←CHECK(SYMB,OBTYPE);		! checks if symbol is present;
	WHILE EL=NULL_RECORD
	     DO BEGIN
		IF OBTYPE=#FR
		   THEN BEGIN
			EL←CHECK(SYMB,#TR);
			IF EL 
			   THEN BEGIN
				EL←CNVRTR(EL,SYMB);
				RETURN(EL);
				END;
			END;
		PRINT($SEMSG[OBTYPE-#MIN]);
		SYMB←RECOVER(SYMB);	! recover can interrupt the loop and abort;
		EL←CHECK(SYMB,OBTYPE);
		END;
	RETURN(EL);	! returns the pointer to the symbol;
	END;

INTERNAL RANY PROCEDURE BELONGS(REFERENCE STRING SYMB; INTEGER OBTYPE);
	RETURN(SYMBOL:OBJECT[BELONGS2(SYMB,OBTYPE)]);

	! checks if the symbol (scalar,vector or rotation) is in $YMTAB.
	  If not inserts it, and returns its pointer;	

FORWARD RPTR(FRAME) PROCEDURE FR_INSERT (REFERENCE STRING SYMB);
RPTR(SYMBOL) PROCEDURE INSERT(STRING SYMB;INTEGER OBTYPE);
	BEGIN
	RPTR(SYMBOL)EL;
	IF OBTYPE=#FR THEN
		BEGIN RPTR(FRAME)FR1; STRING S1;
			S1←SYMB;
			FR1←FR_INSERT(S1);
			RETURN(CHECK(S1,OBTYPE));
		END;
	EL←CHECK(SYMB,OBTYPE);
	IF EL=NULL_RECORD THEN EL←NWR(SYMB,OBTYPE);
	RETURN(EL);
	END;

	! returns the pointer to the frame. If the frame is not present inserts it,
	  otherwise checks its affixment type  and asks for a confirmation if
	  the affixment type is not independent. In that case recovering is allowed;

RPTR(FRAME) PROCEDURE FR_INSERT (REFERENCE STRING SYMB);
	BEGIN "A"
	RPTR(SYMBOL) EL;
	RPTR(FRAME) FRA; STRING TEMP;INTEGER LINK;
	WHILE TRUE 
	     DO	BEGIN "LOOP"
		EL←CHECK(SYMB,#FR);			! if while copying;
		IF $HELP=14 
		   THEN WHILE EL≠NULL_RECORD
			     DO	BEGIN
				! while copying a new frame is required.
				  Recovering is allowed if the frame is existent;
				PRINT($SEMSG[9]);
				SYMB←RECOVER(SYMB);	
				EL←CHECK(SYMB,#FR);
				END;
		IF EL=NULL_RECORD
		   THEN BEGIN
			EL←CHECK(SYMB,#TR);
			IF EL THEN EL←CNVRTR(EL,SYMB)
			   ELSE EL←NWR(SYMB,#FR);		! defines a new frame;
			   RETURN(SYMBOL:OBJECT[EL]);
			END
		   ELSE BEGIN "C"
			FRA←SYMBOL:OBJECT[EL];
			LINK←FRAME:HOWLINKED[FRA];
			! changing values of the frame is allowed if link is #INDLK;
			IF LINK=#INDLK
			   THEN	BEGIN
				$FRLST←NULL;
				RETURN(FRA);
				END
			   ELSE BEGIN
				! otherwise a confirmation is required;
				PRINT(SYMB,
" affixed frame. Changing values can modify the frame tree.",CRLF,
				"You can change the name ");
				TEMP←RECOVER(SYMB);
				! if the name of the frame is the same, 
				  changing values is allowed;
				IF EQU(TEMP ,SYMB) 
				   THEN BEGIN
					$FRLST←NULL;
					RETURN(FRA);
					END
				   ELSE SYMB←TEMP;
				END;
			END "C";
		END "LOOP";
	END "A";

! symbol table: killtree,killvar,reset;

	! removes from $YMTAB all nodes in the subtrees rooted at el;

RECURSIVE PROCEDURE KILLTREE (RPTR(SYMBOL) EL);
	BEGIN
	RPTR(FRAME)TEMP;
	TEMP←SYMBOL:OBJECT[EL];
	DELSYM(EL);				! removes el from $YMTAB;
	TEMP←FRAME:SON[TEMP];
	WHILE TEMP≠NULL_RECORD DO
		BEGIN
		EL←CHECK(FRAME:PNAME[TEMP],#FR);
		KILLTREE(EL);
		TEMP←FRAME:EBRO[TEMP];
		END;
	END;

	! removes the symbol from $YMTAB;

INTERNAL PROCEDURE KILLVAR(REFERENCE STRING VAR;BOOLEAN QUIET(FALSE));
	BEGIN
	RPTR (SYMBOL) EL;RPTR(FRAME)D;INTEGER OBTYPE;
	IF ¬QUIET THEN EL←OLDSYM(VAR,OBTYPE)
	ELSE EL←CHECKTOT(VAR);

	IF EL≠NULL_RECORD THEN
	IF (SYMBOL:INDEX[EL]≤OFFSET[CON_OFFSET,OBTYPE←SYMBOL:TYPE[EL]])
		AND (SYMBOL:OFFSET[EL]<'404
		AND #SC≤OBTYPE≤#FR OR OBTYPE=#MC)
	   THEN PRINT("I cannot delete ",VAR,CRLF)
	   ELSE BEGIN "DEL"
		IF OBTYPE≠#FR 
		   THEN	DELSYM(EL)
		   ELSE BEGIN
			RPTR(FRAME)TEMP;
			TEMP←SYMBOL:OBJECT[EL];
			UNLINK(TEMP);		! unfixes the frame;
			KILLTREE(EL);     		! deletes subtrees rooted in var;
			END;
		$DISPLAYLIST[OBTYPE]←NULL;
		END "DEL";
	END;

	! the procedure deletes all the variables defined by the user. It's
	  called by DELETE with no arguments.;

INTERNAL PROCEDURE RESET;
	BEGIN
	INTEGER IND,TEMP;
	FOR IND←#MIN STEP 1 UNTIL #MAX DO
	    BEGIN INTEGER K,I;
	    WHILE (TEMP←OFFSET[RES_OFFSET,IND])<(K←$ENTRY[IND]) DO
		KILLVAR(SYMBOL:PNAME[$YMTAB[IND,K]]);
	    $DISPLAYLIST[IND]←NULL;
	    END;
	END;
! assignment instruction;

	! assigns to first the value of ob2. If first has not been declared
	  the procedure determines the type of first, according to the value
	  of obtype;

INTERNAL BOOLEAN PROCEDURE PRDECL(RPTR(SYMBOL) OB1);
	RETURN((SYMBOL:OFFSET[OB1]<'400) OR
		(OFFSET[PRG_OFFSET,SYMBOL:TYPE[OB1]]
	<SYMBOL:INDEX[OB1]≤OFFSET[CON_OFFSET,SYMBOL:TYPE[OB1]]));


PROCEDURE ASGEX2(STRING FIRST; RPTR(EXPR$)EEE(NULL_RECORD);
			RPTR(SYMBOL)OB1(NULL_RECORD));
	BEGIN RPTR(EXPR$)E1; INTEGER TY;
	IF EEE THEN E1←EEE ELSE E1←$$GTEXPR;
	IF OB1=NULL_RECORD
	    THEN OB1←INSERT(FIRST,TY←EXPR$:TYPE[E1])
	    ELSE BEGIN
		IF (TY←SYMBOL:TYPE[OB1])=#FR AND EXPR$:TYPE[E1]=#TR THEN
			EXPR$:TYPE[E1]←#FR
		   ELSE IF TY=#TR AND EXPR$:TYPE[E1]=#FR
			THEN CNVRTR(OB1,FIRST)
		   ELSE IF EXPR$:TYPE[E1]≠TY THEN ERROR("INCOMAPTABILE TYPE ASSIGNMENT");
		END;
	$$PCODE←$ASGPCODE(E1,OB1);
	END;

PROCEDURE ASGEX3(RPTR(EXPR$)E);
	$$PCODE←$AASGPCODE(E,$$GTEXPR);
! tree operations:   afx_node,ufx_node,copycode,copy,copy_tree;

	! affixes the frame pointed by n to the frame pointed by d, as indicated
	  by how;
INTERNAL PROCEDURE AFX_NODE(RPTR(FRAME)N,D;INTEGER HOW);
	BEGIN
	LINKFR(N,D);				! sets links in frame tree;
	FRAME:HOWLINKED[N]←HOW;
	END;

INTERNAL PROCEDURE UFX_NODE(RPTR(FRAME)EL1,EL2);
	BEGIN
	UNLINK(EL1);				! breaks links in tree;
	FRAME:HOWLINKED[EL1]←#INDLK;
	LINKFR(EL1,F_WRLD);			! sets new links;
	END;

RECURSIVE STRING PROCEDURE COPY_TREE(RPTR(FRAME) ND; STRING PREFIX;
		REFERENCE STRING NEWNAME);
	BEGIN
	! copies the structure rooted at ND ;
 	RPTR(FRAME)KIDS;
	STRING RETSTR;
	STRING OLDNAME,LEAVE,ONAME;
	ONAME←OLDNAME←FRAME:PNAME[ND];
	! constructs the new name of the frame: if the name of the copied
	  frame contains an underscore, the part before it is substituted
	  by prefix, otherwise prefix is prefixed;
	LEAVE←SCAN(OLDNAME,$DSHTAB,$BRCHR);	
	IF $BRCHR≠0 
 	   THEN NEWNAME←PREFIX&OLDNAME
	   ELSE NEWNAME←PREFIX&LEAVE;
	FR_INSERT(NEWNAME);			! inserts a new frame;
 	KIDS←FRAME:SON[ND];
	RETSTR←NEWNAME&"←"&ONAME&";";
	WHILE KIDS≠NULL_RECORD DO
		BEGIN
		STRING NEWKID;
		RETSTR←RETSTR©_TREE(KIDS,PREFIX,NEWKID);
		RETSTR←RETSTR&" AFFIX "&NEWKID&" TO "&NEWNAME;
		IF FRAME:HOWLINKED[KIDS]≠#RGDLK THEN
			RETSTR←RETSTR&" NONRIGIDLY";
		RETSTR←RETSTR&";";
		KIDS←FRAME:EBRO[KIDS];
		END;
	RETURN(RETSTR);
	END;

	! copies the subtree rooted at startfr and affixes it to finalfr.
	  Prefix is used to build the names of the new frames;
STRING PROCEDURE PCOPY(RPTR(FRAME) STARTFR,FINALFR; STRING PREFIX);
	BEGIN
	STRING S,NEWROOT;
	S←COPY_TREE(STARTFR,PREFIX,NEWROOT);
	RETURN(S&"AFFIX "&NEWROOT&" TO "&FRAME:PNAME[FINALFR]&
		" AT "&FRAME:PNAME[STARTFR]&";");
	END;

	! merges the subtrees under startfr as sons of finalfr. Prefix is
	  used to build the names of new frames;

STRING PROCEDURE PMERGE(RPTR(FRAME) STARTFR,FINALFR;STRING PREFIX);
	BEGIN
	STRING S,NEWROOT;
	RPTR(FRAME)TEMP,BROTHER;
	TEMP←FRAME:SON[STARTFR];
	S←NULL;
	DO	BEGIN
		BROTHER←FRAME:EBRO[TEMP];
		S←S©_TREE(TEMP,PREFIX,NEWROOT);		! copies one subtree;
		S←S&"AFFIX "&NEWROOT&" TO "&FRAME:PNAME[FINALFR]&" AT "&
			FRAME:PNAME[STARTFR]&"→"&FRAME:PNAME[TEMP]&";";
		TEMP←BROTHER;
		END
	UNTIL TEMP=NULL_RECORD;
	RETURN(S);
	END;

	! executes copy or merge operation on frame1 and frame2. Name indicates
	  the required operation(copy/merge);

INTERNAL PROCEDURE COPYCODE(STRING NAME,FRAME1,FRAME2);
	BEGIN
	RPTR(FRAME) FR1,FR2;STRING PREFIX,ANSWER;
	FR1←BELONGS (FRAME1,#FR);			! frame1 must be a frame;
	FR2←BELONGS (FRAME2,#FR);			! frame2 must be a frame;
	! chooses the prefix for the new names: if the name of frame2 contains an
	  underscore takes  the part before it, otherwise takes the first three
	  characters (long names) or all the name and asks for a confirmation;
	ANSWER←FRAME:PNAME[FR2];	
	PREFIX←SCAN(ANSWER,$DSHTAB,$BRCHR);
	IF $BRCHR=0 AND
	   LENGTH(PREFIX)>5 THEN
	   PREFIX←FRAME:PNAME[FR2] [1 FOR 3];
	PRINT("it's OK to prefix to the new names ");
	PREFIX←RECOVER(PREFIX)&"_";
	$ALLOW←$ALLOW+1; ! the matching $ALLOW←$ALLOW-1 is taken care of by ASKUSER;
	IF EQU(NAME,"COPY")
	   THEN ASKUSER(PCOPY(FR1,FR2,PREFIX)&"UPDATE;")
	   ELSE ASKUSER(PMERGE(FR1,FR2,PREFIX)&"UPDATE;");
	END;
! arm interactions:  read_pos,readarm,frasg,arm_check;

	! assigns the value of pos(pointer or arm) to the frame fra. If direct
	  is indicated uses it to set the rotation part;

	! returns the pointer to the input device pos (arm or pointer);

RPTR (FRAME) PROCEDURE INPT_DEV(REFERENCE STRING POS);
	BEGIN
	RPTR(FRAME) FROM;
	IF EQU(POS,"BARM")
	   THEN RETURN(F_BARM)
	   ELSE IF EQU(POS,"YARM")
		   THEN RETURN(F_YARM)
		   ELSE BEGIN
			FROM←BELONGS(POS,#FR);
			WHILE FROM≠F_BARM AND FROM≠F_YARM
			   DO	BEGIN
			        PRINT ($SEMSG[12]);
				POS←RECOVER(POS);
				FROM←BELONGS (POS,#FR);
				END;
			RETURN(FROM);
			END;
	END;

	! reads the position of the arm from, or of the arm with pointer;

PROCEDURE READ_DEV(RPTR(FRAME) FROM);
	print("dummy call to get value of the frame");

	! reads the position of the device pos (arm or pointer);

PROCEDURE INPT(REFERENCE STRING POS);
	BEGIN
	RPTR(FRAME)FROM;
	FROM←INPT_DEV(POS);
	READ_DEV(FROM);
	END;


	! returns the pointer to the arm affixed to obj;
INTERNAL RPTR(FRAME) PROCEDURE ARM_CHECK(RPTR(FRAME) OBJ);
	BEGIN
	RPTR(FRAME) TEMP;
	TEMP←OBJ;
	WHILE TEMP≠F_WRLD DO
		IF EQU(FRAME:PNAME[TEMP],"BARM")
		   OR EQU(FRAME:PNAME[TEMP],"YARM") THEN RETURN(TEMP)
			ELSE TEMP←FRAME:DAD[TEMP];
	ABORT1(FRAME:PNAME[OBJ],$SEMSG[8]);
	END;
! arm interactions:  fconstructproc;

	! reads an axis name and returns its number:
	  xhat=0,yhat=1,zhat=2;

INTEGER PROCEDURE INPT_AXIS(REFERENCE STRING AXIS);
	BEGIN
	LABEL LL;
LL:	AXIS←RECOVER(AXIS);
	IF EQU(AXIS[2 TO ∞],"HAT") THEN RETURN(AXIS - "X")
		   ELSE BEGIN
			PRINT($SYNMSG[17],$SYNMSG[25],CRLF,"Try again ");
			GOTO LL;
			END;
	END;
	
IFC FALSE THENC
RPTR(TRANS) ARRAY T_CSTR[1:3]; 
		! used by CONSTRUCT instruction;

	! performs a construct instruction, without arguments;

PROCEDURE FCONSTRUCTPROC;
	BEGIN
	RPTR(FRAME) ELF;RPTR(TRANS)XFE;INTEGER I;
	RPTR(FRAME) FROM;STRING POS,ANSWER,FIRST;
	RPTR(VECTOR) V1,V2,V3;
	PRELOAD_WITH 
	    	"move arm to the origin of the frame"&CRLF,
		"move arm to the axis ",
		"move arm to the plane ";
		OWN STRING ARRAY INFORM[1:3];
	STRING AXIS;INTEGER F_AXIS,S_AXIS;

	$ALLOW←$ALLOW+1;
	GTOKEN;
	IF #TOKEN≠UNDECLARED_TYPE THEN ERROR("Need undeclared token for FCONSTRUCT")
		ELSE FIRST←TOKEN;

	AXIS←NULL;
	IF F_POINTER=NULL_RECORD
	   THEN PRINT("pointer is not defined cannot be used",CRLF)
	   ELSE POS←"POINTER";
	PRINT("three positions are required",CRLF);
	FOR I←1 STEP 1 UNTIL 3 DO
		BEGIN
	! determination of the input device required;
	   	PRINT("position ",I," read on ");
		POS←RECOVER(POS);
		FROM←INPT_DEV(POS);			! checks the input device;
	! determination of the positions for reading;
		PRINT(INFORM[I]);
		IF I=2
		   THEN F_AXIS←INPT_AXIS(AXIS)
		ELSE IF I=3
		   THEN BEGIN
			PRINT(AXIS," - ");
			AXIS←NULL;
			S_AXIS←INPT_AXIS(AXIS);
			IF S_AXIS=F_AXIS THEN ABORT1($SEMSG[13]);
			END;
	! reading of the arm position;
		PRINT("type <cr> when the arm is at the desired position");
		ANSWER←INCHRW;
		IF ANSWER=CR 
		   THEN ANSWER←INCHRW
		   ELSE	ABORT1($SEMSG[13]);
	 	READ_DEV(FROM);				! raads the appropriate arm pos.;
		T_CSTR[I]←ABSLOC(FROM);
		END;

	! extraction of translation part;
	V1←TPOS(T_CSTR[1]);
	V2←TPOS(T_CSTR[2]);
	V3←TPOS(T_CSTR[3]);
	
	XFE←VVVTR(V1,V2,V3,F_AXIS,S_AXIS);
	ELF←FR_INSERT(FIRST);			! inserts the new frame;
	ABSSET(ELF,XFE);			! sets the new value;
	$ALLOW←$ALLOW-1;
	IFC #DISPL THENC UPDATE;ENDC	
	END;
ENDC
! system facilities: editcode,renmcode,bailcode,qbailcode;
IFC NOT #NOFUNCT THENC
PROCEDURE UNRAVEL_SYMBOLS_USED(RPTR(expr)SYMBOLSUSED;RPTR(SYMBOL)EL);
	BEGIN RPTR(SYMBOL)EL2;
	RPTR(expr)SY,SY2; INTEGER NARGS; NARGS←0;
	SY←SYMBOLSUSED;
	WHILE SY≠NULL_RECORD DO BEGIN NARGS←NARGS+1; SY←EXPR:NEXT[SY]; END;
	IF NARGS>0 THEN
		BEGIN RPTR(EXPR)ARRAY SS[1:NARGS]; INTEGER I;
		SY←SYMBOLSUSED;
		FOR I←1 STEP 1 UNTIL NARGS DO
			BEGIN
			INTEGER J,JJ;
			SS[I]←SY;
			EL2←EXPR:PTR[SY];
			ADDSYMUSED(EL,EL2);
			SY←EXPR:NEXT[SY2←SY];
			EXPR:NEXT[SY2]←NULL_RECORD;
			END;
		MEMORY[LOCATION(SYMBOL:USES[EL])]←MEMORY[LOCATION(SS)];
		MEMORY[LOCATION(ss)]←0;
		SYMBOL:NUSES[EL]←NARGS;
		END;
	END;
ENDC
	! edits values of the variable var;
INTERNAL PROCEDURE EDITCODE (STRING VAR);
	BEGIN
	RPTR(SYMBOL)EL;INTEGER OBTYPE;STRING FBODY;
	RPTR(SCALAR,VECTOR,TRANS,FRAME,ROT,MACRO) TEMP;

	NOEXPAND ← TRUE;

	EL←OLDSYM(VAR,OBTYPE);				! var must exist in $YMTAB;
	TEMP←SYMBOL:OBJECT[EL];

	IF OBTYPE = #MC
	   THEN BEGIN
		INTEGER BRCHAR;
		STRING OLD_STRING,NEW_STRING,LINE_STRING;
		OLD_STRING← "DEFINE "&MACRO:HEAD[SYMBOL:OBJECT[EL]]
			&" = "&CVSYM(EL,EDIT_D)&";";
		NEW_STRING←LINE_STRING←NULL;
		WHILE OLD_STRING DO
			BEGIN LINE_STRING←SCAN(OLD_STRING,$CRTAB,BRCHAR);
			LODED(LINE_STRING&CR);
			NEW_STRING←NEW_STRING&INCHWL&CRLF;
			END;
		ASKUSER(NEW_STRING);
		DELSYM(EL);
		END
	ELSE  BEGIN
	SETFORMAT(0,7);	
	IF PRDECL(EL) THEN ABORT1(VAR,$SEMSG[14]);
	   IF OBTYPE=#FR AND FRAME:HOWLINKED[TEMP]≠#INDLK
	      THEN PRINT("values of ",VAR," are relative to ",
		FRAME:PNAME[FRAME:DAD[TEMP]],CRLF);
!		ELSE IF OBTYPE=#FN THEN VAR←FUNCTION:HEAD[TEMP];
	   PRINT("value of ",VAR," = ");
	IF OBTYPE=#PR THEN ERROR("Cant edit procedures yet");
	LODED(CVSYM(EL,EDIT_D)&CR);
	ASKUSER;
IFC NOT #NOFUNCT THENC
	   IF OBTYPE=#FN THEN α RPTR(EXPR)SYMBOLSUSED;
				TEMP1←FNEXPR(TEMP,FBODY,SYMBOLSUSED);
				BEGIN RPTR(EXPR) T;
					T←NEW_RECORD(EXPR);
					EXPR:PTR[T]←TREE:DATA[TEMP1];
					EXPR:TYPE[T]←TREE:DTYPE[TEMP1];
					FUNCTION:EXPR[TEMP]←T;
				END;
			DELSYMREF(EL);
			UNRAVEL_SYMBOLS_USED(SYMBOLSUSED,EL);
			 FUNCTION:BODY[TEMP]←FBODY; β
		ELSE ENDC ASGEX2(VAR);
	SETFORMAT(0,3);
	END;

	NOEXPAND ← FALSE;

	END;


	! allows renaming a variable;
INTERNAL PROCEDURE RENMCODE(STRING VAR);
	BEGIN
	RPTR(SYMBOL)OLDEL;INTEGER OBTYPE;STRING NEW;
	STRING SFSF;

	NOEXPAND ← TRUE;

	SFSF ← VAR;
 	OLDEL←OLDSYM(VAR,OBTYPE);		! var must exist in $YMTAB;
	PRINT("new name = ");
	NEW←RECOVER(VAR);			! reads the new name;
	IF NEW NEQ SFSF
	       THEN NEW←NEWSYM(NEW);			! checks new doesn't exist;
	IFC #OUTPT THENC IF $OUT THEN CPRINT($TTYCH,NEW,CRLF);ENDC
	SYMBOL:PNAME[OLDEL]←NEW;		! changes the name in record symbol;
	IF OBTYPE=#FR 
	   THEN  FRAME:PNAME[SYMBOL:OBJECT[OLDEL]]←NEW;
	$DISPLAYLIST[OBTYPE]←NULL;

	NOEXPAND ← FALSE;

	END;
IFC #DEBUG THENC
INTERNAL PROCEDURE BAILCODE;
		BEGIN
		GTOKEN(FALSE);
		IF TOKEN="("
		  THEN BEGIN
			INTEGER BRCHAR, COUNT;
			COUNT←1;
			DO BEGIN
			IF (BRCHAR←READTILL("()"))="(" THEN COUNT←COUNT+1
				ELSE COUNT←COUNT-1;
			!!QUERY←!!QUERY&TOKEN&BRCHAR;
			END UNTIL COUNT=0;
			!!QUERY←!!QUERY[1 TO ∞-1];
			END
		  ELSE STOKEN←TRUE;
		BRK_N;
		BAIL;
		END;

INTERNAL PROCEDURE QBAILCODE;
    begin integer chn, count, brchar, eof, all;
    open(chn ← getchan, "DSK", 1, 2, 0, count, brchar, eof);
    if ¬eof then
	begin
	lookup(chn, "QUERY.TXT", eof);
	count ← 1000;  setbreak(all ← getbreak, ff, null, "IS");
	if ¬eof then __query ← input(chn, all);
	end;
    outstr("!!query ← """ & __query & """" & crlf);
    release(chn); relbreak(all);
    bail;
    end;

INTEGER !!i1,!!i2,!!i3,!!i4,!!i5,!!i6;
RANY	!!r1,!!r2,!!r3,!!r4,!!r5,!!r6;

PROCEDURE DINIT;
	BEGIN !!i1←!!i2←!!i3←!!i4←!!i5←!!i6←0;
	!!r1←!!r2←!!r3←!!r4←!!r5←!!r6←null_record;
	END;

REQUIRE DINIT INITIALIZATION;

ELSEC
INTERNAL PROCEDURE BAILCODE;
	NOTAVAILCALL;

INTERNAL PROCEDURE QBAILCODE;
	NOTAVAILCALL;
ENDC
! parse procedures: other;

INTERNAL PROCEDURE DEFLT(STRING HOW);
	BEGIN
	IF EQU(OLDCMD,"OPEN") OR EQU(OLDCMD,"CLOSE")
	   THEN OPENING(OLDCMD,OLDOBJ,HOW)
	ELSE IF EQU(OLDCMD,"MOVEX")OR EQU(OLDCMD,"MOVEY")OR EQU(OLDCMD,"MOVEZ")
		THEN IF HOW="BY"
			THEN ALONGPROC(OLDCMD[5 FOR 1],OLDOBJ)
			ELSE ERROR($SYNMSG[10],$SYNMSG[25])
	ELSE IF EQU(OLDCMD,"DRIVE")
		THEN JTMOVE("BJT",HOW,CVD(OLDOBJ))
	ELSE IF EQU(OLDCMD,"MOVE") 
		THEN IF EQU(HOW,"BY") THEN PBYPROC ELSE PTOPROC;
	END;

PROCEDURE ASGMNT(STRING FIRST;RPTR(SYMBOL)S);
	IF (S≠NULL_RECORD) AND PRDECL(S) THEN
		ERROR("You cannot change the value of "&FIRST)
		ELSE ASGEX2(FIRST,NULL_RECORD,S);
	
INTERNAL PROCEDURE OTHER;
	BEGIN STRING FIRST; RPTR(SYMBOL)SS; RPTR(EXPR$)EE;
	$HELP←41; FIRST←TOKEN;  EE←NULL_RECORD;
 	IF (SS←TOKENPTR)≠NULL_RECORD THEN
		BEGIN IF SYMBOL:ACCESS[TOKENPTR]=#ARRAY
			THEN EE←AREF(TOKENPTR,XCHNGE)
			ELSE IF SYMBOL:ACCESS[TOKENPTR]=#PROCEDURE
				THEN BEGIN $$PCODE←PREF(TOKENPTR);
				RETURN; END;
		END;
	GTOKEN;
	IF TOKEN="←"
	   THEN IF EE THEN ASGEX3(EE) ELSE ASGMNT(FIRST,SS)
	   ELSE ERROR($SYNMSG[32],NULL);
	END;

! main program;


WHILE TRUE DO
	BEGIN 
	$COMPILE←0;		! set interpreter mode;
	$LEVEL←0;		! indicate it is top level ;
	$TMPOFF←$SYMOFF;
	CURPROC←NULL_RECORD;
	CURBLOCK←NULL_RECORD;
	STBEGIN←TRUE;			! waiting for a new command;
	$CLNSAVE←NULL;			! get rid of the saved string;
	PARSE;				! parses the instruction;
	CHKESC_I;
MAINL: STOKEN←FALSE;
	IFC #WRIST THENC IF WSTPTR THEN RWRIST("READ");	ENDC
	IF !LINE THEN PRINT(CRLF,"LAST STATEMENT: ",$CLNSAVE);
	END;